/*
* Copyright 2004 Original mockejb authors.
* Copyright 2007 Nuxeo SAS.
*
* This file is derived from mockejb-0.6-beta2
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.nuxeo.common.jndi;
import java.util.Collections;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Properties;
import java.util.Vector;
import javax.naming.Binding;
import javax.naming.CompositeName;
import javax.naming.CompoundName;
import javax.naming.Context;
import javax.naming.ContextNotEmptyException;
import javax.naming.InvalidNameException;
import javax.naming.Name;
import javax.naming.NameAlreadyBoundException;
import javax.naming.NameNotFoundException;
import javax.naming.NameParser;
import javax.naming.NamingEnumeration;
import javax.naming.NamingException;
import javax.naming.NoPermissionException;
import javax.naming.NotContextException;
import javax.naming.Reference;
import javax.naming.spi.NamingManager;
/**
* Provides implementation of <code>javax.naming.Context</code> interface for
* hierarchical in memory single-namespace naming system.
* A name in the <code>NamingContext</code> namespace is a sequence of one or more
* atomic names, relative to a root initial context.
* When a name consist of more than one atomic names it is a <code>CompoundName</code>
* where atomic names are separated with separator character - '/' or '.'.
* It is possible to use both separator characters in the same name. In such cases
* any occurrences of '.' are replaced with '/' before parsing.
* <p>
* Leading and terminal components of a <code>CompoundName</code> can not be empty -
* for example "name1/name2/name3" is a valid name, while the following names are
* not valid - "/name1/name2/name3", "name1/name2/name3/", "/name1/name2/name3/".
* If such name is passed, all empty leading/terminal components will be removed
* before the name is actually used (this will not affect the original value) -
* from the above three examples the actual name will be "name1/name2/name3".
* If a name contains intermediate empty components (for example "a//b") then
* <code>InvalidNameException</code> will be thrown.
* <p>
* Composite names (instances of <code>CompositeName</code>) must contain zero or one
* component from the <code>NamingContext</code> namespace.
* <p>
* The namespace of <code>NamingContext</code> can be represented as a tree of atomic names.
* Each name is bound to an instance of NamingContext (subcontext) or to an arbitrary object.
* Each subcontext has collection of names bound to other subcontexts or arbitrary objects.
* <p>
* When instance of <code>Name</code> is used as parameter to any of the
* NamingContext methods, if the object is not <code>CompositeName</code> then
* it is assumed that it is <code>CompoundName</code>
* <p>
* Example:
* <pre><code>
* myContext = initialContext.lookup("foo");
* myObject = myContext.lookup("bar");
* </code>
* is equivalent to
* <code>myObject = initialContext.lookup("foo/bar");</code>
* </pre>
* <p>
* Instances of <code>NamingContext</code> are created only through
* <code>NamingContextFactory</code>, when <code>InitialContext</code> is instantiated.
* <p>
* If a remote context is provided, this class will search in that remote context if the
* object is not found locally.
* <p>
* For overloaded methods that accept name as <code>String</code> or
* <code>Name</code> only the version for <code>Name</code> is documented.
* The <code>String</code> version creates <code>CompoundName</code>, from
* the string name passed as parameter, and calls the <code>Name</code> version of
* the same method.
*
* @author Alexander Ananiev
* @author Dimitar Gospodinov
*/
public class NamingContext implements Context {
private static final String ROOT_CONTEXT_NAME = "ROOT";
// NamingContext supports single naming scheme and all instances use the same parser.
private static final NameParser nameParser = new NamingContextNameParser();
/**
* NamingContext name parser.
*
* @author Dimitar Gospodinov
*/
public static class NamingContextNameParser implements NameParser {
private static final Properties syntax = new Properties();
static {
syntax.put("jndi.syntax.direction", "left_to_right");
syntax.put("jndi.syntax.separator", "/");
syntax.put("jndi.syntax.ignorecase", "false");
syntax.put("jndi.syntax.trimblanks", "yes");
}
/**
* Parses <code>name</code> into <code>CompoundName</code>
* using the following <code>CompoundName</code> properties:
* <pre>
* jndi.syntax.direction = "left_to_right"
* jndi.syntax.separator = "/"
* jndi.syntax.ignorecase = "false"
* jndi.syntax.trimblanks = "yes"
* </pre>
* Any characters '.' in the name <code>name</code> will be replaced with the
* separator character specified above, before parsing.
*
* @param name name to parse
* @throws NamingException if a naming error occurs
*/
public Name parse(String name) throws NamingException {
return new CompoundName(name.replace('.', '/'), syntax);
}
}
/**
* Map of objects registered for this context representing the local
* context.
*/
private final Map<Object, Object> objects = Collections.synchronizedMap(new HashMap<Object, Object>());
/** Parent Context of this Context. */
private final NamingContext parent;
/** Atomic name of this Context. */
private final String contextName;
/**
* Container context used for delegated lookups.
*/
private final Context remoteContext;
// Shows if this context has been destroyed
private boolean isDestroyed;
/**
* Creates a new instance of the context. This class can only be
* instantiated by its factory.
*
* @param remoteContext remote context that NamingContext will delegate to if
* it fails to lookup an object locally
*/
protected NamingContext(Context remoteContext) {
this(remoteContext, null, ROOT_CONTEXT_NAME);
}
/**
* Creates new instance of <code>NamingContext</code>.
*
* @param remoteContext remote context that NamingContext will delegate to if
* it fails to lookup an object locally
* @param parent parent context of this context. <code>null</code> if this
* is the root context.
* @param name atomic name for this context
*/
private NamingContext(Context remoteContext, NamingContext parent, String name) {
this.remoteContext = remoteContext;
this.parent = parent;
contextName = name;
isDestroyed = false;
}
/**
* Not implemented.
*
* @see javax.naming.Context#addToEnvironment(java.lang.String, java.lang.Object)
*/
public Object addToEnvironment(String arg0, Object arg1)
throws NamingException {
throw new RuntimeException("Method not implemented");
}
/**
* Binds object <code>obj</code> to name <code>name</code> in this
* context. Intermediate contexts that do not exist will be created.
*
* @param name name of the object to bind
* @param obj object to bind. Can be <code>null</code>.
*
* @throws NoPermissionException if this context has been destroyed
* @throws InvalidNameException if <code>name</code> is empty or is
* <code>CompositeName</code> that spans more than one naming
* system
* @throws NotContextException if <code>name</code> has more than one
* atomic name and intermediate atomic name is bound to object
* that is not context.
*
* @see javax.naming.Context#bind(javax.naming.Name, java.lang.Object)
*/
public void bind(Name name, Object obj) throws NamingException {
checkIsDestroyed();
// Do not check for already bound name. Simply replace the existing value.
rebind(name, obj);
}
/**
* Binds object <code>obj</code> to name <code>name</code> in this
* context.
*
* @param name name of the object to add
* @param obj object to bind
* @throws NamingException if naming error occurs
* @see #bind(Name, Object)
*/
public void bind(String name, Object obj) throws NamingException {
bind(nameParser.parse(name), obj);
}
/**
* Does nothing.
*
* @see javax.naming.Context#close()
*/
public void close() throws NamingException {
}
/**
* Returns composition of <code>prefix</code> and <code>name</code>.
*
* @param name name relative to this context
* @param prefix name of this context
* @see javax.naming.Context#composeName(javax.naming.Name,
* javax.naming.Name)
*/
public Name composeName(Name name, Name prefix) throws NamingException {
checkIsDestroyed();
/*
* We do not want to modify any of the parameters (JNDI requirement).
* Clone <code>prefix</code> to satisfy the requirement.
*/
Name parsedPrefix = getParsedName((Name) prefix.clone());
Name parsedName = getParsedName(name);
return parsedPrefix.addAll(parsedName);
}
/**
* Composes the name of this context with a name relative to this context.
* Given a name (name) relative to this context, and the name (prefix)
* of this context relative to one of its ancestors, this method returns
* the composition of the two names using the syntax appropriate for
* the naming system(s) involved.
* <p>
* Example:
* <pre>
* composeName("a","b") b/a
* composeName("a","") a
* </pre>
*
* @param name name relative to this context
* @param prefix name of this context
* @see javax.naming.Context#composeName(java.lang.String, java.lang.String)
*/
public String composeName(String name, String prefix)
throws NamingException {
checkIsDestroyed();
return composeName(nameParser.parse(name), nameParser.parse(prefix)).toString();
}
/**
* Creates subcontext with name <code>name</code>, relative to this
* Context.
*
* @param name subcontext name.
* @return new subcontext named <code>name</code> relative to this context
* @throws NoPermissionException if this context has been destroyed
* @throws InvalidNameException if <code>name</code> is empty or is
* <code>CompositeName</code> that spans more than one naming
* system
* @throws NameAlreadyBoundException if <code>name</code> is already bound
* in this Context
* @throws NotContextException if any intermediate name from
* <code>name</code> is not bound to instance of
* <code>javax.naming.Context</code>
* @see javax.naming.Context#createSubcontext(javax.naming.Name)
*/
public Context createSubcontext(Name name)
throws NamingException {
checkIsDestroyed();
Name parsedName = getParsedName(name);
if (parsedName.isEmpty() || parsedName.get(0).length() == 0) {
throw new InvalidNameException("Name can not be empty!");
}
String subContextName = parsedName.get(0);
Object boundObject = objects.get(parsedName.get(0));
if (parsedName.size() == 1) {
// Check if <code>name</code> is already in use
if (boundObject == null) {
Context subContext =
new NamingContext(remoteContext, this, subContextName);
objects.put(subContextName, subContext);
return subContext;
} else {
throw new NameAlreadyBoundException(
"Name " + subContextName + " is already bound!");
}
} else {
if (boundObject instanceof Context) {
// Let the subcontext create new subcontext
return ((Context) boundObject).createSubcontext(
parsedName.getSuffix(1));
} else {
throw new NotContextException(
"Expected Context but found " + boundObject);
}
}
}
/**
* Creates subcontext with name <code>name</code>, relative to this
* Context.
*
* @param name subcontext name
* @return new subcontext named <code>name</code> relative to this context
* @throws NamingException if naming error occurs
* @see #createSubcontext(javax.naming.Name)
*/
public Context createSubcontext(String name) throws NamingException {
return createSubcontext(nameParser.parse(name));
}
/**
* Destroys subcontext with name <code>name</code>. The subcontext must
* be empty, otherwise <code>ContextNotEmptyException</code> is thrown.
* <p>
* Once a context is destroyed, the instance should not be used.
*
* @param name subcontext to destroy
* @throws NoPermissionException if this context has been destroyed
* @throws InvalidNameException if <code>name</code> is empty or is
* <code>CompositeName</code> that spans more than one naming
* system
* @throws ContextNotEmptyException if Context <code>name</code> is not
* empty
* @throws NameNotFoundException if subcontext with name <code>name</code>
* can not be found
* @throws NotContextException if <code>name</code> is not bound to
* instance of <code>NamingContext</code>
* @see javax.naming.Context#destroySubcontext(javax.naming.Name)
*/
public void destroySubcontext(Name name) throws NamingException {
checkIsDestroyed();
Name parsedName = getParsedName(name);
if (parsedName.isEmpty() || parsedName.get(0).length() == 0) {
throw new InvalidNameException("Name can not be empty!");
}
String subContextName = parsedName.get(0);
Object boundObject = objects.get(subContextName);
if (boundObject == null) {
throw new NameNotFoundException(
"Name " + subContextName + "not found in the context!");
}
if (!(boundObject instanceof NamingContext)) {
throw new NotContextException();
}
NamingContext contextToDestroy = (NamingContext) boundObject;
if (parsedName.size() == 1) {
/*
* Check if the Context to be destroyed is empty.
* Can not destroy non-empty Context.
*/
if (contextToDestroy.objects.isEmpty()) {
objects.remove(subContextName);
contextToDestroy.destroyInternal();
} else {
throw new ContextNotEmptyException("Can not destroy non-empty Context!");
}
} else {
// Let the subcontext destroy the context
((Context) boundObject).destroySubcontext(
parsedName.getSuffix(1));
}
}
/**
* Destroys subcontext with name <code>name</code>.
*
* @param name name of subcontext to destroy
* @throws NamingException if naming error occurs
* @see #destroySubcontext(javax.naming.Name)
*/
public void destroySubcontext(String name) throws NamingException {
destroySubcontext(nameParser.parse(name));
}
/**
* Not implemented.
*
* @see javax.naming.Context#getEnvironment()
*/
public Hashtable<?, ?> getEnvironment() throws NamingException {
throw new RuntimeException("Method not implemented");
}
/**
* Not implemented.
*
* @see javax.naming.Context#getNameInNamespace()
*/
public String getNameInNamespace() throws NamingException {
return getCompoundStringName();
}
/**
* Retrieves name parser used to parse context with name <code>name</code>.
*
* @param name context name
* @return <code>NameParser</code>
* @throws NoPermissionException if this context has been destroyed
* @throws NamingException if any other naming error occurs
* @see javax.naming.Context#getNameParser(javax.naming.Name)
*/
public NameParser getNameParser(Name name) throws NamingException {
checkIsDestroyed();
return nameParser;
}
/**
* Retrieves name parser used to parse context with name <code>name</code>.
*
* @param name context name
* @return <code>NameParser</code>
* @throws NamingException if naming error occurs
* @see #getNameParser(javax.naming.Name)
*/
public NameParser getNameParser(String name) throws NamingException {
checkIsDestroyed();
return nameParser;
}
/**
* The same as <code>listBindings(String)</code>.
*
* @param name name of Context, relative to this Context
* @return <code>NamingEnumeration</code> of all name-class pairs. Each
* element from the enumeration is instance of
* <code>NameClassPair</code>
* @throws NamingException if naming error occurs
* @see #listBindings(javax.naming.Name)
*/
@SuppressWarnings("unchecked")
public NamingEnumeration list(Name name) throws NamingException {
return listBindings(name);
}
/**
* The same as <code>listBindings(String)</code>.
*
* @param name name of Context, relative to this Context
* @return <code>NamingEnumeration</code> of all name-class pairs. Each
* element from the enumeration is instance of
* <code>NameClassPair</code>
* @throws NamingException if naming error occurs
* @see #listBindings(java.lang.String)
*/
@SuppressWarnings("unchecked")
public NamingEnumeration list(String name) throws NamingException {
return list(nameParser.parse(name));
}
/**
* Lists all bindings for Context with name <code>name</code>. If
* <code>name</code> is empty, then this Context is assumed.
*
* @param name name of Context, relative to this Context
* @return <code>NamingEnumeration</code> of all name-object pairs. Each
* element from the enumeration is instance of <code>Binding</code>
* @throws NoPermissionException if this context has been destroyed
* @throws InvalidNameException if <code>name</code> is
* <code>CompositeName</code> that spans more than one naming
* system
* @throws NameNotFoundException if <code>name</code> can not be found
* @throws NotContextException component of <code>name</code> is not bound
* to instance of <code>NamingContext</code>, when
* <code>name</code> is not an atomic name
* @throws NamingException if any other naming error occurs
* @see javax.naming.Context#listBindings(javax.naming.Name)
*/
public NamingEnumeration<Binding> listBindings(Name name) throws NamingException {
checkIsDestroyed();
Name parsedName = getParsedName(name);
if (parsedName.isEmpty()) {
Vector<Binding> bindings = new Vector<Binding>();
for (Object o : objects.keySet()) {
String bindingName = (String) o;
bindings.addElement(
new Binding(bindingName, objects.get(bindingName)));
}
return new NamingEnumerationImpl(bindings);
} else {
Object subContext = objects.get(parsedName.get(0));
if (subContext instanceof Context) {
return ((Context) subContext).listBindings(parsedName.getSuffix(1));
}
if (subContext == null
&& !objects.containsKey(parsedName.get(0))) {
throw new NameNotFoundException("Name " + name + " not found");
} else {
throw new NotContextException(
"Expected Context but found " + subContext);
}
}
}
/**
* Lists all bindings for Context with name <code>name</code>. If
* <code>name</code> is empty then this Context is assumed.
*
* @param name name of Context, relative to this Context
* @return <code>NamingEnumeration</code> of all name-object pairs. Each
* element from the enumeration is instance of <code>Binding</code>
* @throws NamingException if naming error occurs
* @see #listBindings(javax.naming.Name)
*/
public NamingEnumeration<Binding> listBindings(String name) throws NamingException {
return listBindings(nameParser.parse(name));
}
/**
* Looks up object with name <code>name</code> in this context. If the
* object is not found and the remote context was provided, calls the remote
* context to lookup the object.
*
* @param name name to look up
* @return object reference bound to name <code>name</code>
* @throws NoPermissionException if this context has been destroyed
* @throws InvalidNameException if <code>name</code> is
* <code>CompositeName</code> that spans more than one naming
* system
* @throws NameNotFoundException if <code>name</code> can not be found
* @throws NotContextException component of <code>name</code> is not bound
* to instance of <code>NamingContext</code>, when
* <code>name</code> is not atomic name.
* @throws NamingException if any other naming error occurs
* @see javax.naming.Context#lookup(javax.naming.Name)
*/
public Object lookup(Name name) throws NamingException {
checkIsDestroyed();
try {
return lookupInternal(name);
} catch (NameNotFoundException ex) {
// Shall we delegate?
if (remoteContext != null) {
return remoteContext.lookup(name);
} else {
throw new NameNotFoundException("Name " + name + " not found. ");
}
}
}
private Object lookupInternal(Name name) throws NamingException {
Name parsedName = getParsedName(name);
String nameComponent = parsedName.get(0);
Object res = objects.get(nameComponent);
// if not found
if (!objects.containsKey(nameComponent)) {
throw new NameNotFoundException("Name " + name + " not found.");
}
// if this is a compound name
else if (parsedName.size() > 1) {
if (res instanceof NamingContext) {
res = ((NamingContext) res).lookupInternal(parsedName
.getSuffix(1));
} else {
throw new NotContextException("Expected NamingContext but found "
+ res);
}
}
// if this is a reference
else if (res instanceof Reference) {
try {
Hashtable<String, Object> env = new Hashtable<String, Object>();
res = NamingManager.getObjectInstance(res, name, this,
env);
if (res != null) {
objects.put(nameComponent, res);
}
} catch (NamingException e) {
throw e;
} catch (Exception e) {
throw new NamingException(e.getMessage());
}
}
return res;
}
/**
* Looks up the object in this context. If the object is not found and the
* remote context was provided, calls the remote context to lookup the
* object.
*
* @param name object to search
* @return object reference bound to name <code>name</code>
* @throws NamingException if naming error occurs
* @see #lookup(javax.naming.Name)
*/
public Object lookup(String name) throws NamingException {
checkIsDestroyed();
try {
return lookupInternal(name);
} catch (NameNotFoundException ex) {
// Shall we delegate?
if (remoteContext != null) {
return remoteContext.lookup(name);
} else {
throw new NameNotFoundException("Name " + name + " not found. ");
}
}
}
private Object lookupInternal(String name) throws NamingException {
return lookupInternal(nameParser.parse(name));
}
/**
* Not implemented.
*
* @see javax.naming.Context#lookupLink(javax.naming.Name)
*/
public Object lookupLink(Name arg0) throws NamingException {
throw new RuntimeException("Method not implemented");
}
/**
* Not implemented.
*
* @see javax.naming.Context#lookupLink(java.lang.String)
*/
public Object lookupLink(String arg0) throws NamingException {
throw new RuntimeException("Method not implemented");
}
/**
* Rebinds object <code>obj</code> to name <code>name</code>. If there
* is existing binding it will be overwritten.
*
* @param name name of the object to rebind
* @param obj object to add. Can be <code>null</code>
* @throws NoPermissionException if this context has been destroyed
* @throws InvalidNameException if <code>name</code> is empty or is
* <code>CompositeName</code> that spans more than one naming
* system
* @throws NotContextException if <code>name</code> has more than one
* atomic name and intermediate context is not found
* @throws NamingException if any other naming error occurs
* @see javax.naming.Context#rebind(javax.naming.Name, java.lang.Object)
*/
public void rebind(Name name, Object obj) throws NamingException {
checkIsDestroyed();
Name parsedName = getParsedName(name);
if (parsedName.isEmpty() || parsedName.get(0).length() == 0) {
throw new InvalidNameException("Name can not be empty!");
}
String nameToBind = parsedName.get(0);
if (parsedName.size() == 1) {
objects.put(nameToBind, obj);
} else {
Object boundObject = objects.get(nameToBind);
if (boundObject instanceof Context) {
/*
* Let the subcontext bind the object.
*/
((Context) boundObject).bind(parsedName.getSuffix(1), obj);
} else {
if (boundObject == null) {
// Create new subcontext and let it do the binding
Context sub = createSubcontext(nameToBind);
sub.bind(parsedName.getSuffix(1), obj);
} else {
throw new NotContextException("Expected Context but found "
+ boundObject);
}
}
}
}
/**
* Same as bind except that if <code>name</code> is already bound in the
* context, it will be re-bound to object <code>obj</code>.
*
* @param name name of the object to rebind
* @param obj object to add. Can be <code>null</code>
* @throws NamingException if naming error occurs
* @see #rebind(javax.naming.Name, Object)
*/
public void rebind(String name, Object obj) throws NamingException {
rebind(nameParser.parse(name), obj);
}
/**
* Not implemented.
*
* @see javax.naming.Context#removeFromEnvironment(java.lang.String)
*/
public Object removeFromEnvironment(String arg0) throws NamingException {
throw new RuntimeException("Method not implemented");
}
/**
* Not implemented.
*
* @see javax.naming.Context#rename(javax.naming.Name, javax.naming.Name)
*/
public void rename(Name arg0, Name arg1) throws NamingException {
throw new RuntimeException("Method not implemented");
}
/**
* Not implemented.
*
* @see javax.naming.Context#rename(java.lang.String, java.lang.String)
*/
public void rename(String arg0, String arg1) throws NamingException {
throw new RuntimeException("Method not implemented");
}
/**
* Removes <code>name</code> and its associated object from the context.
*
* @param name name to remove
* @throws NoPermissionException if this context has been destroyed
* @throws InvalidNameException if <code>name</code> is empty or is
* <code>CompositeName</code> that spans more than one naming
* system
* @throws NameNotFoundException if intermediate context can not be found
* @throws NotContextException if <code>name</code> has more than one
* atomic name and intermediate context is not found
* @throws NamingException if any other naming exception occurs
* @see javax.naming.Context#unbind(javax.naming.Name)
*/
public void unbind(Name name) throws NamingException {
checkIsDestroyed();
Name parsedName = getParsedName(name);
if (parsedName.isEmpty() || parsedName.get(0).length() == 0) {
throw new InvalidNameException("Name can not be empty!");
}
String nameToRemove = parsedName.get(0);
if (parsedName.size() == 1) {
objects.remove(nameToRemove);
} else {
Object boundObject = objects.get(nameToRemove);
if (boundObject instanceof Context) {
/*
* Let the subcontext do the unbind
*/
((Context) boundObject).unbind(parsedName.getSuffix(1));
} else {
if (!objects.containsKey(nameToRemove)) {
throw new NameNotFoundException("Can not find " + name);
}
throw new NotContextException("Expected Context but found "
+ boundObject);
}
}
}
/**
* Removes object from the object map.
*
* @param name object to remove
* @throws NamingException if naming error occurs
* @see #unbind(javax.naming.Name)
*/
public void unbind(String name) throws NamingException {
unbind(nameParser.parse(name));
}
// ** Non-standard methods
/**
* Checks if this context has been destroyed. <code>isDestroyed</code> is
* set to <code>true</code> when a context is destroyed by calling
* <code>destroySubcontext</code> method.
*
* @throws NoPermissionException if this context has been destroyed
*/
private void checkIsDestroyed() throws NoPermissionException {
if (isDestroyed) {
throw new NoPermissionException("Can not invoke operations on destroyed context!");
}
}
/**
* Marks this context as destroyed.
* Method called only by <code>destroySubcontext</code>.
*/
private void destroyInternal() {
isDestroyed = true;
}
/**
* Parses <code>name</code> which is <code>CompositeName</code> or
* <code>CompoundName</code>. If <code>name</code> is not
* <code>CompositeName</code> then it is assumed to be
* <code>CompoundName</code>.
* <p>
* If the name contains leading and/or terminal empty components, they will
* not be included in the result.
*
* @param name <code>Name</code> to parse
* @return parsed name as instance of <code>CompoundName</code>
* @throws InvalidNameException if <code>name</code> is
* <code>CompositeName</code> and spans more than one name
* space
* @throws NamingException if any other naming exception occurs
*/
private static Name getParsedName(Name name) throws NamingException {
Name result;
if (name instanceof CompositeName) {
if (name.isEmpty()) {
// Return empty CompositeName
result = nameParser.parse("");
} else if (name.size() > 1) {
throw new InvalidNameException("Multiple name systems are not supported!");
}
result = nameParser.parse(name.get(0));
} else {
result = (Name) name.clone();
}
while (!result.isEmpty()) {
if (result.get(0).length() == 0) {
result.remove(0);
continue;
}
if (result.get(result.size() - 1).length() == 0) {
result.remove(result.size() - 1);
continue;
}
break;
}
// Validate that there are not intermediate empty components.
// Skip first and last element, they are valid
for (int i = 1; i < result.size() - 1; i++) {
if (result.get(i).length() == 0) {
throw new InvalidNameException("Empty intermediate components are not supported!");
}
}
return result;
}
/**
* Returns the compound string name of this context.
* Suppose a/b/c/d is the full name and this context is "c".
* It's compound string name is a/b/c
*
* @return compound string name of the context
*/
String getCompoundStringName() throws NamingException {
//StringBuffer compositeName = new StringBuffer();
String compositeName="";
NamingContext curCtx = this;
while (!curCtx.isRootContext()) {
compositeName = composeName(compositeName, curCtx.contextName);
curCtx = curCtx.parent;
}
return compositeName;
}
/**
* Returns parent context of this context.
*/
NamingContext getParentContext() {
return parent;
}
/**
* Returns the "atomic" (as opposed to "composite") name of the context.
*
* @return name of the context
*/
String getAtomicName(){
return contextName;
}
/**
* Returns true if this context is the root context.
*
* @return true if the context is the root context
*/
boolean isRootContext(){
return parent == null;
}
private static class NamingEnumerationImpl implements NamingEnumeration<Binding> {
private final Vector<Binding> elements;
private int currentElement;
NamingEnumerationImpl(Vector<Binding> elements) {
this.elements = elements;
currentElement = 0;
}
public void close() {
currentElement = 0;
elements.clear();
}
public boolean hasMore() {
return hasMoreElements();
}
public boolean hasMoreElements() {
if (currentElement < elements.size()) {
return true;
}
close();
return false;
}
public Binding next() {
return nextElement();
}
public Binding nextElement() {
if (hasMoreElements()) {
return elements.get(currentElement++);
}
throw new NoSuchElementException();
}
}
@Override
public String toString() {
try {
return getCompoundStringName();
} catch (NamingException e) {
return super.toString();
}
}
}